package buka.oversea.node3.controller;

import buka.oversea.node3.model.RaftNode;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/balance")
public class NodeController {
    private final RaftNode raftNode;
    private final RestTemplate restTemplate;

    public NodeController(RaftNode raftNode, RestTemplate restTemplate) {
        this.raftNode = raftNode;
        this.restTemplate = restTemplate;
    }

    @PostMapping("/{account}/{op}/{amount}")
    public ResponseEntity<String> updateBalance(
            @PathVariable String account,
            @PathVariable String op,
            @PathVariable int amount) {

        // 验证操作类型
        if (!"add".equals(op) && !"sub".equals(op)) {
            return ResponseEntity.badRequest().body("Invalid operation: " + op);
        }

        // 创建操作对象
        RaftNode.Operation operation = new RaftNode.Operation(op, account, amount);

        // 如果是Leader直接处理
        if (raftNode.getState() == RaftNode.State.LEADER) {
            System.out.println("当前Leader节点为:" + raftNode.getNodeId());
            if (raftNode.addOperation(operation)) {
                return ResponseEntity.ok("Operation committed");
            }
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Operation failed");
        }
        // 如果是Follower重定向到Leader
        else if (raftNode.getLeaderId() != null) {
            System.out.println("当前Leader节点为:" + raftNode.getLeaderId());
            String leaderUrl = "http://" + raftNode.getLeaderId() + "/api/balance/" +
                    account + "/" + op + "/" + amount;
            return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT)
                    .header("Location", leaderUrl)
                    .build();
        }
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body("No leader available");
    }

    @GetMapping("/{account}")
    public ResponseEntity<Integer> getBalance(@PathVariable String account) {
        return ResponseEntity.ok(raftNode.getBalance(account));
    }

    // Raft RPC处理端点
    @PostMapping("/raft/requestVote")
    public ResponseEntity<Map<String, Object>> handleRequestVote(@RequestBody Map<String, Object> request) {
        int term = ((Number) request.get("term")).intValue();
        String candidateId = (String) request.get("candidateId");
        int lastLogIndex = ((Number) request.get("lastLogIndex")).intValue();
        int lastLogTerm = ((Number) request.get("lastLogTerm")).intValue();


        RaftNode.VoteResult result = raftNode.requestVote(term, candidateId, lastLogIndex, lastLogTerm);

        Map<String, Object> response = new HashMap<>();
        response.put("term", result.term());
        response.put("voteGranted", result.voteGranted());
        return ResponseEntity.ok(response);
    }

    @PostMapping("/raft/appendEntries")
    public ResponseEntity<Map<String, Object>> handleAppendEntries(@RequestBody Map<String, Object> request) {
        int term = ((Number) request.get("term")).intValue();
        String leaderId = (String) request.get("leaderId");
        int prevLogIndex = ((Number) request.get("prevLogIndex")).intValue();
        int prevLogTerm = ((Number) request.get("prevLogTerm")).intValue();
        int leaderCommit = ((Number) request.get("leaderCommit")).intValue();

        List<RaftNode.LogEntry> entries = null;
        if (request.containsKey("entries") && request.get("entries") != null) {
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> entriesList = (List<Map<String, Object>>) request.get("entries");
            entries = new ArrayList<>();
            for (Map<String, Object> entryMap : entriesList) {
                int entryTerm = ((Number) entryMap.get("term")).intValue();

                @SuppressWarnings("unchecked")
                Map<String, Object> opMap = (Map<String, Object>) entryMap.get("operation");

                String type = (String) opMap.get("type");
                String account = (String) opMap.get("account");
                int amount = ((Number) opMap.get("amount")).intValue();

                entries.add(new RaftNode.LogEntry(
                        entryTerm,
                        new RaftNode.Operation(type, account, amount)
                ));
            }
        }

        RaftNode.AppendResult result = raftNode.appendEntries(
                term, leaderId, prevLogIndex, prevLogTerm, entries, leaderCommit
        );

        Map<String, Object> response = new HashMap<>();
        response.put("term", result.term());
        response.put("success", result.success());
        return ResponseEntity.ok(response);
    }
}
